package com.gitee.wsl.android.ext

import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.BitmapFactory
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.LinearGradient
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Point
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.Shader
import android.media.MediaMetadataRetriever
import android.os.Build
import android.util.Base64
import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.FloatRange
import androidx.core.graphics.scale
import androidx.core.net.toFile
import androidx.core.net.toUri
import com.gitee.wsl.android.file.FileManager
import timber.log.Timber
import java.io.BufferedOutputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStream
import kotlin.math.roundToInt


object BitmapExt {

   // =======
    // = 圆角 =
    // =======

    /**
     * 图片圆角处理 ( 非圆形 )
     * <pre>
     * 以宽高中最小值设置为圆角尺寸, 如果宽高一致, 则处理为圆形图片
    </pre> *
     * @param bitmap 待操作源图片
     * @return 圆角处理后的图片
     */
    fun roundCorner(bitmap: Bitmap): Bitmap {
        return  roundCorner(bitmap, bitmap.width.coerceAtMost(bitmap.height).toFloat())
    }

    /**
     * 图片圆角处理 ( 非圆形 )
     * @param bitmap 待操作源图片
     * @param pixels 圆角大小
     * @return 圆角处理后的图片
     */
    fun roundCorner(
        bitmap: Bitmap,
        pixels: Float
    ): Bitmap {
        // 创建一个同源图片一样大小的矩形, 用于把源图片绘制到这个矩形上
        val rect = Rect(0, 0, bitmap.width, bitmap.height)
        val rectF = RectF(rect) // 创建一个精度更高的矩形, 用于画出圆角效果
        val paint = Paint()
        paint.isAntiAlias = true
        paint.color = -0xbdbdbe // 设置画笔的颜色为不透明的灰色
        val newBitmap = Bitmap.createBitmap(
            bitmap.width, bitmap.height,
            Bitmap.Config.ARGB_8888
        )
        val canvas = Canvas(newBitmap)
        canvas.drawARGB(0, 0, 0, 0)
        canvas.drawRoundRect(rectF, pixels, pixels, paint)
        // 绘制底圆后, 进行合并 ( 交集处理 )
        paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
        canvas.drawBitmap(bitmap, rect, rect, paint)
        return newBitmap
    }

    // =

    // =
    /**
     * 图片圆角处理 ( 非圆形, 只有 leftTop、rightTop )
     * @param bitmap 待操作源图片
     * @param pixels 圆角大小
     * @return 圆角处理后的图片
     */
    fun roundCornerTop(
        bitmap: Bitmap,
        pixels: Float
    ): Bitmap? {
        return roundCorner(bitmap, pixels, booleanArrayOf(true, true, true, false))
    }

    /**
     * 图片圆角处理 ( 非圆形, 只有 leftBottom、rightBottom )
     * @param bitmap 待操作源图片
     * @param pixels 圆角大小
     * @return 圆角处理后的图片
     */
    fun roundCornerBottom(
        bitmap: Bitmap,
        pixels: Float
    ): Bitmap? {
        return roundCorner(bitmap, pixels, booleanArrayOf(true, false, true, true))
    }

    /**
     * 图片圆角处理 ( 非圆形 )
     * <pre>
     * 只要左上圆角: new boolean[] {true, true, false, false};
     * 只要右上圆角: new boolean[] {false, true, true, false};
     *
     *
     * 只要左下圆角: new boolean[] {true, false, false, true};
     * 只要右下圆角: new boolean[] {false, false, true, true};
    </pre> *
     * @param bitmap     待操作源图片
     * @param pixels     圆角大小
     * @param directions 需要圆角的方向 [left, top, right, bottom]
     * @return 圆角处理后的图片
     */
    fun roundCorner(
        bitmap: Bitmap,
        pixels: Float,
        directions: BooleanArray?
    ): Bitmap? {
        
        if (directions == null || directions.size != 4) return null
        // 创建一个同源图片一样大小的矩形, 用于把源图片绘制到这个矩形上
        val rect = Rect(0, 0, bitmap.width, bitmap.height)
        val rectF = RectF(rect) // 创建一个精度更高的矩形, 用于画出圆角效果

        // =============
        // = 圆角方向控制 =
        // =============
        if (!directions[0]) {
            rectF.left -= pixels
        }
        if (!directions[1]) {
            rectF.top -= pixels
        }
        if (!directions[2]) {
            rectF.right += pixels
        }
        if (!directions[3]) {
            rectF.bottom += pixels
        }
        val paint = Paint()
        paint.isAntiAlias = true
        paint.color = -0xbdbdbe // 设置画笔的颜色为不透明的灰色
        val newBitmap = Bitmap.createBitmap(
            bitmap.width, bitmap.height,
            Bitmap.Config.ARGB_8888
        )
        val canvas = Canvas(newBitmap)
        canvas.drawARGB(0, 0, 0, 0)
        canvas.drawRoundRect(rectF, pixels, pixels, paint)
        // 绘制底圆后, 进行合并 ( 交集处理 )
        paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
        canvas.drawBitmap(bitmap, rect, rect, paint)
        return newBitmap
    }

    // =======
    // = 圆形 =
    // =======

    /**
     * 图片圆形处理
     * @param bitmap 待操作源图片
     * @return 圆形处理后的图片
     */
    fun round(bitmap: Bitmap): Bitmap {
        return round(bitmap, 0, 0)
    }

    /**
     * 图片圆形处理
     * @param bitmap      待操作源图片
     * @param borderSize  边框尺寸
     * @param borderColor 边框颜色
     * @return 圆形处理后的图片
     */
    fun round(
        bitmap: Bitmap,
         borderSize: Int,
        @ColorInt borderColor: Int
    ): Bitmap {
        val width = bitmap.width
        val height = bitmap.height
        val size = width.coerceAtMost(height)
        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        val center = size / 2f
        val rectF = RectF(0f, 0f, width.toFloat(), height.toFloat())
        rectF.inset((width - size) / 2f, (height - size) / 2f)
        val matrix = Matrix()
        matrix.setTranslate(rectF.left, rectF.top)
        if (width != height) {
            matrix.preScale(size.toFloat() / width, size.toFloat() / height)
        }
        val shader = BitmapShader(
            bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP
        )
        shader.setLocalMatrix(matrix)
        paint.shader = shader
        val newBitmap = Bitmap.createBitmap(width, height, bitmap.config)
        val canvas = Canvas(newBitmap)
        canvas.drawRoundRect(rectF, center, center, paint)
        if (borderSize > 0) {
            paint.shader = null
            paint.color = borderColor
            paint.style = Paint.Style.STROKE
            paint.strokeWidth = borderSize.toFloat()
            val radius = center - borderSize / 2f
            canvas.drawCircle(width / 2f, height / 2f, radius, paint)
        }
        return newBitmap
    }

    // ===============
    // = 圆角、圆形边框 =
    // ===============

    /**
     * 添加圆角边框
     * @param bitmap       源图片
     * @param borderSize   边框尺寸
     * @param color        边框颜色
     * @param cornerRadius 圆角半径
     * @return 圆角边框图
     */
    fun addCornerBorder(
        bitmap: Bitmap,
         borderSize: Int,
        @ColorInt color: Int,
        @FloatRange(from = 0.0) cornerRadius: Float
    ): Bitmap {
        return addBorder(bitmap, borderSize, color, false, cornerRadius)
    }

    /**
     * 添加圆形边框
     * @param bitmap     源图片
     * @param borderSize 边框尺寸
     * @param color      边框颜色
     * @return 圆形边框图
     */
    fun addCircleBorder(
        bitmap: Bitmap,
        borderSize: Int,
        @ColorInt color: Int
    ): Bitmap {
        return addBorder(bitmap, borderSize, color, true, 0f)
    }

    /**
     * 添加边框
     * @param bitmap       待操作源图片
     * @param borderSize   边框尺寸
     * @param color        边框颜色
     * @param isCircle     是否画圆
     * @param cornerRadius 圆角半径
     * @return 添加边框后的图片
     */
    fun addBorder(
        bitmap: Bitmap,
        borderSize: Int,
        @ColorInt color: Int,
        isCircle: Boolean,
        cornerRadius: Float
    ): Bitmap {
        val newBitmap = bitmap.copy(bitmap.config, true)
        val width = newBitmap.width
        val height = newBitmap.height
        val canvas = Canvas(newBitmap)
        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        paint.color = color
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = borderSize.toFloat()
        if (isCircle) {
            val radius = width.coerceAtMost(height) / 2f - borderSize / 2f
            canvas.drawCircle(width / 2f, height / 2f, radius, paint)
        } else {
            val halfBorderSize = borderSize shr 1
            val rectF = RectF(
                halfBorderSize.toFloat(), halfBorderSize.toFloat(),
                (
                        width - halfBorderSize).toFloat(),
                (
                        height - halfBorderSize
                        ).toFloat()
            )
            canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint)
        }
        return newBitmap
    }

    // =======
    // = 水印 =
    // =======
    /**
     * 添加文字水印
     * @param bitmap   待操作源图片
     * @param content  水印文本
     * @param textSize 水印字体大小 pixel
     * @param color    水印字体颜色
     * @param x        起始坐标 x
     * @param y        起始坐标 y
     * @return 添加文字水印后的图片
     */
    fun addTextWatermark(
        bitmap: Bitmap,
        content: String,
        textSize: Float,
        @ColorInt color: Int,
        x: Float,
        y: Float
    ): Bitmap {
        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        paint.color = color
        paint.textSize = textSize
        val bounds = Rect()
        paint.getTextBounds(content, 0, content.length, bounds)
        val newBitmap = bitmap.copy(bitmap.config, true)
        val canvas = Canvas(newBitmap)
        canvas.drawText(content, x, y + textSize, paint)
        return newBitmap
    }

    /**
     * 添加图片水印
     * @param bitmap    待操作源图片
     * @param watermark 水印图片
     * @param x         起始坐标 x
     * @param y         起始坐标 y
     * @param alpha     透明度
     * @return 添加图片水印后的图片
     */
    fun addImageWatermark(
        bitmap: Bitmap,
        watermark: Bitmap,
        x: Int,
        y: Int,
        alpha: Int
    ): Bitmap? {
        val newBitmap = bitmap.copy(bitmap.config, true)
       
            val paint = Paint(Paint.ANTI_ALIAS_FLAG)
            val canvas = Canvas(newBitmap)
            paint.alpha = alpha
            canvas.drawBitmap(watermark, x.toFloat(), y.toFloat(), paint)
        
        return newBitmap
    }
    
    // =======
    // = 压缩 =
    // =======

    /**
     * 按缩放比例压缩
     * <pre>
     * 可搭配 [ScaleUtils] 工具类使用
    </pre> *
     * @param bitmap 待操作源图片
     * @param scaleX 横向缩放比例 ( 缩放宽度倍数 )
     * @param scaleY 纵向缩放比例 ( 缩放高度倍数 )
     * @return 缩放比例压缩后的图片
     */
    fun compressByScale(
        bitmap: Bitmap,
        scaleX: Float,
        scaleY: Float
    ): Bitmap {
        return bitmap.scale(scaleX.toInt(), scaleY.toInt())
    }


    // =
    /**
     * 按质量压缩
     * @param bitmap  待操作源图片
     * @param quality 质量
     * @return 质量压缩过的图片
     */
    fun compressByQuality(
        bitmap: Bitmap,
         quality: Int
    ): Bitmap? {
        return compressByQuality(
            bitmap, CompressFormat.JPEG, quality, null
        )
    }

    /**
     * 按质量压缩
     * @param bitmap  待操作源图片
     * @param quality 质量
     * @param options [BitmapFactory.Options]
     * @return 质量压缩过的图片
     */
    fun compressByQuality(
        bitmap: Bitmap,
        quality: Int,
        options: BitmapFactory.Options?
    ): Bitmap? {
        return compressByQuality(bitmap, CompressFormat.JPEG, quality, options)
    }

    /**
     * 按质量压缩
     * @param bitmap  待操作源图片
     * @param format  图片压缩格式
     * @param quality 质量
     * @param options [BitmapFactory.Options]
     * @return 质量压缩过的图片
     */
    fun compressByQuality(
        bitmap: Bitmap,
        format: CompressFormat?,
        quality: Int,
        options: BitmapFactory.Options?
    ): Bitmap? {
        if ( format == null) return null
        try {
            val baos = ByteArrayOutputStream()
            bitmap.compress(format, quality, baos)
            val data = baos.toByteArray()
            return BitmapFactory.decodeByteArray(data, 0, data.size, options)
        } catch (e: java.lang.Exception) {
           Timber.d( e, "compressByQuality")
        }
        return null
    }

    // =
    /**
     * 按质量压缩 ( 图片大小 )
     * @param bitmap      待操作源图片
     * @param maxByteSize 允许最大值字节数
     * @return 质量压缩过的图片
     */
    fun compressByByteSize(
        bitmap: Bitmap,
        maxByteSize: Long
    ): Bitmap? {
        return compressByByteSize(
            bitmap, CompressFormat.JPEG, maxByteSize, null
        )
    }

    /**
     * 按质量压缩 ( 图片大小 )
     * @param bitmap      待操作源图片
     * @param format      图片压缩格式
     * @param maxByteSize 允许最大值字节数
     * @return 质量压缩过的图片
     */
    fun compressByByteSize(
        bitmap: Bitmap,
        format: CompressFormat,
        maxByteSize: Long
    ): Bitmap? {
        return compressByByteSize(bitmap, format, maxByteSize, null)
    }

    /**
     * 按质量压缩 ( 图片大小 )
     * @param bitmap      待操作源图片
     * @param format      图片压缩格式
     * @param maxByteSize 允许最大值字节数
     * @param options     [BitmapFactory.Options]
     * @return 质量压缩过的图片
     */
    fun compressByByteSize(
        bitmap: Bitmap,
        format: CompressFormat,
        maxByteSize: Long,
        options: BitmapFactory.Options?
    ): Bitmap? {
        if ( maxByteSize <= 0) return null
        try {
            val baos = ByteArrayOutputStream()
            bitmap.compress(format, 100, baos)
            val data: ByteArray
            if (baos.size() <= maxByteSize) { // 最好质量的不大于最大字节, 则返回最佳质量
                data = baos.toByteArray()
            } else {
                baos.reset()
                bitmap.compress(format, 0, baos)
                if (baos.size() >= maxByteSize) { // 最差质量不小于最大字节, 则返回最差质量
                    data = baos.toByteArray()
                } else { // 二分法寻找最佳质量
                    var start = 0
                    var end = 100
                    var mid = 0
                    while (start < end) {
                        mid = (start + end) / 2
                        baos.reset()
                        bitmap.compress(format, mid, baos)
                        val len = baos.size()
                        if (len.toLong() == maxByteSize) {
                            break
                        } else if (len > maxByteSize) {
                            end = mid - 1
                        } else {
                            start = mid + 1
                        }
                    }
                    if (end == mid - 1) {
                        baos.reset()
                        bitmap.compress(format, start, baos)
                    }
                    data = baos.toByteArray()
                }
            }
            return BitmapFactory.decodeByteArray(data, 0, data.size, options)
        } catch (e: java.lang.Exception) {
           Timber.d( e, "compressByByteSize")
        }
        return null
    }

    // =
    /**
     * 按采样大小压缩
     * @param bitmap     待操作源图片
     * @param sampleSize 采样率大小
     * @return 按采样率压缩后的图片
     */
    fun compressBySampleSize(
        bitmap: Bitmap,
        sampleSize: Int
    ): Bitmap? {
        return compressBySampleSize(bitmap, CompressFormat.JPEG, sampleSize)
    }

    /**
     * 按采样大小压缩
     * @param bitmap     待操作源图片
     * @param format     图片压缩格式
     * @param sampleSize 采样率大小
     * @return 按采样率压缩后的图片
     */
    fun compressBySampleSize(
        bitmap: Bitmap,
        format: CompressFormat?,
        sampleSize: Int
    ): Bitmap? {
        if ( format == null) return null
        try {
            val options = BitmapFactory.Options()
            options.inSampleSize = sampleSize
            val baos = ByteArrayOutputStream()
            bitmap.compress(format, 100, baos)
            val data = baos.toByteArray()
            return BitmapFactory.decodeByteArray(data, 0, data.size, options)
        } catch (e: java.lang.Exception) {
           Timber.d( e, "compressBySampleSize")
        }
        return null
    }

    // =
    /**
     * 按采样大小压缩
     * @param bitmap    待操作源图片
     * @param maxWidth  最大宽度
     * @param maxHeight 最大高度
     * @return 按采样率压缩后的图片
     */
    fun compressBySampleSize(
        bitmap: Bitmap,
        maxWidth: Int,
        maxHeight: Int
    ): Bitmap? {
        return compressBySampleSize(
            bitmap, CompressFormat.JPEG, maxWidth, maxHeight
        )
    }

    /**
     * 按采样大小压缩
     * @param bitmap    待操作源图片
     * @param format    图片压缩格式
     * @param maxWidth  最大宽度
     * @param maxHeight 最大高度
     * @return 按采样率压缩后的图片
     */
    fun compressBySampleSize(
        bitmap: Bitmap,
        format: CompressFormat,
        maxWidth: Int,
        maxHeight: Int
    ): Bitmap? {
        try {
            // 获取宽高信息
            val options = BitmapFactory.Options()
            options.inJustDecodeBounds = true
            val baos = ByteArrayOutputStream()
            bitmap.compress(format, 100, baos)

            // 进行采样压缩
            val data = baos.toByteArray()
            BitmapFactory.decodeByteArray(data, 0, data.size, options)
            options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight)
            options.inJustDecodeBounds = false
            return BitmapFactory.decodeByteArray(data, 0, data.size, options)
        } catch (e: java.lang.Exception) {
           Timber.d( e, "compressBySampleSize")
        }
        return null
    }

    // =
    /**
     * 计算采样大小
     * <pre>
     * 最大宽高只是阀值, 实际算出来的图片将小于等于这个值
    </pre> *
     * @param options   [BitmapFactory.Options]
     * @param maxWidth  最大宽度
     * @param maxHeight 最大高度
     * @return 采样大小
     */
    fun calculateInSampleSize(
        options: BitmapFactory.Options?,
        maxWidth: Int,
        maxHeight: Int
    ): Int {
        if (options == null) return 0
        var height = options.outHeight
        var width = options.outWidth
        var inSampleSize = 1
        while (height > maxHeight || width > maxWidth) {
            height = height shr 1
            width = width shr 1
            inSampleSize = inSampleSize shl 1
        }
        return inSampleSize
    }

    /**
     * 计算最佳压缩质量值
     * <pre>
     * 搭配 [ImageUtils.saveBitmapToSDCard] 等需要传入 quality 方法使用
    </pre> *
     * @param bitmap      待操作源图片
     * @param format      图片压缩格式
     * @param maxByteSize 允许最大值字节数
     * @return 最佳压缩质量值
     */
    fun calculateQuality(
        bitmap: Bitmap,
        format: CompressFormat,
        maxByteSize: Long
    ): Int {
        if ( maxByteSize <= 0) return -1
        try {
            val baos = ByteArrayOutputStream()
            bitmap.compress(format, 100, baos)
            return if (baos.size() <= maxByteSize) { // 最好质量的不大于最大字节, 则返回最佳质量
                100
            } else {
                baos.reset()
                bitmap.compress(format, 0, baos)
                if (baos.size() >= maxByteSize) { // 最差质量不小于最大字节, 则返回最差质量
                    0
                } else { // 二分法寻找最佳质量
                    var start = 0
                    var end = 100
                    var mid = 0
                    while (start < end) {
                        mid = (start + end) / 2
                        baos.reset()
                        bitmap.compress(format, mid, baos)
                        val len = baos.size()
                        if (len.toLong() == maxByteSize) {
                            return mid
                        } else if (len > maxByteSize) {
                            end = mid - 1
                        } else {
                            start = mid + 1
                        }
                    }
                    if (end == mid - 1) {
                        start
                    } else mid
                }
            }
        } catch (e: java.lang.Exception) {
           Timber.d( e, "calculateQuality")
        }
        return -1
    }

    // ============
    // = 视频缩略图 =
    // ============
    /**
     * 获取视频缩略图
     * @param path 视频路径
     * @return [Bitmap]
     */
    fun getVideoThumbnail(path: String): Bitmap? {
        return getVideoThumbnail(path, -1)
    }

    /**
     * 获取视频缩略图
     * <pre>
     * // 获取视频的长度 ( 单位为毫秒 )
     * String duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
     * // 缩放缩略图
     * ThumbnailUtils.extractThumbnail(bitmap,  width, height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
     * // 获取视频缩略图
     * ThumbnailUtils.createVideoThumbnail(path, MediaStore.Video.Thumbnails.MICRO_KIND);
    </pre> *
     * @param path   视频路径
     * @param millis 对应毫秒视频帧
     * @return [Bitmap]
     */
    fun getVideoThumbnail(
        path: String,
        millis: Long
    ): Bitmap? {
        val retriever = MediaMetadataRetriever()
        try {
            // 设置视频路径
            if (path.toUri().toFile().exists()) {
                retriever.setDataSource(path)
            } else {
                retriever.setDataSource(path, HashMap())
            }
            return retriever.getFrameAtTime(millis)
        } catch (e: java.lang.Exception) {
           Timber.d( e, "getVideoThumbnail")
        } finally {
            try {
                retriever.release()
            } catch (ignored: java.lang.Exception) {
            }
        }
        return null
    }


    // ==========
    // = 滤镜效果 =
    // ==========
    /**
     * 怀旧效果处理
     * @param bitmap 待操作源图片
     * @return 怀旧效果处理后的图片
     */
    fun nostalgic(bitmap: Bitmap): Bitmap? {
        try {
            val width = bitmap.width
            val height = bitmap.height
            var pixColor: Int
            var pixR: Int
            var pixG: Int
            var pixB: Int
            var newR: Int
            var newG: Int
            var newB: Int
            val pixels = IntArray(width * height)
            bitmap.getPixels(pixels, 0, width, 0, 0, width, height)
            for (i in 0 until height) {
                for (k in 0 until width) {
                    pixColor = pixels[width * i + k]
                    pixR = Color.red(pixColor)
                    pixG = Color.green(pixColor)
                    pixB = Color.blue(pixColor)
                    newR = (0.393 * pixR + 0.769 * pixG + 0.189 * pixB).toInt()
                    newG = (0.349 * pixR + 0.686 * pixG + 0.168 * pixB).toInt()
                    newB = (0.272 * pixR + 0.534 * pixG + 0.131 * pixB).toInt()
                    val newColor = Color.argb(
                        255, Math.min(newR, 255),
                        Math.min(newG, 255), Math.min(newB, 255)
                    )
                    pixels[width * i + k] = newColor
                }
            }
            val newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
            newBitmap.setPixels(pixels, 0, width, 0, 0, width, height)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "nostalgic")
        }
        return null
    }

    /**
     * 光照效果处理
     * @param bitmap  待操作源图片
     * @param centerX 光源在 X 轴的位置
     * @param centerY 光源在 Y 轴的位置
     * @return 光照效果处理后的图片
     */
    fun sunshine(
        bitmap: Bitmap,
        centerX: Int,
        centerY: Int
    ): Bitmap? {
        try {
            val width = bitmap.width
            val height = bitmap.height
            var pixColor: Int
            var pixR: Int
            var pixG: Int
            var pixB: Int
            var newR: Int
            var newG: Int
            var newB: Int
            val radius = Math.min(centerX, centerY)
            val strength = 150f // 光照强度 100 ~ 150
            val pixels = IntArray(width * height)
            bitmap.getPixels(pixels, 0, width, 0, 0, width, height)
            var pos: Int
            var i = 1
            val length = height - 1
            while (i < length) {
                var k = 1
                val len = width - 1
                while (k < len) {
                    pos = i * width + k
                    pixColor = pixels[pos]
                    pixR = Color.red(pixColor)
                    pixG = Color.green(pixColor)
                    pixB = Color.blue(pixColor)
                    newR = pixR
                    newG = pixG
                    newB = pixB

                    // 计算当前点到光照中心的距离, 平面座标系中求两点之间的距离
                    val distance = (Math.pow(
                        (centerY - i).toDouble(),
                        2.0
                    ) + Math.pow((centerX - k).toDouble(), 2.0)).toInt()
                    if (distance < radius * radius) {
                        // 按照距离大小计算增加的光照值
                        val result =
                            (strength * (1.0 - Math.sqrt(distance.toDouble()) / radius)).toInt()
                        newR = pixR + result
                        newG = pixG + result
                        newB = pixB + result
                    }
                    newR = Math.min(255, Math.max(0, newR))
                    newG = Math.min(255, Math.max(0, newG))
                    newB = Math.min(255, Math.max(0, newB))
                    pixels[pos] = Color.argb(255, newR, newG, newB)
                    k++
                }
                i++
            }
            val newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
            newBitmap.setPixels(pixels, 0, width, 0, 0, width, height)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "sunshine")
        }
        return null
    }

    /**
     * 底片效果处理
     * @param bitmap 待操作源图片
     * @return 底片效果处理后的图片
     */
    fun film(bitmap: Bitmap): Bitmap? {
        try {
            // ARGB 的最大值
            val MAX_VALUE = 255
            val width = bitmap.width
            val height = bitmap.height
            var pixColor: Int
            var pixR: Int
            var pixG: Int
            var pixB: Int
            var newR: Int
            var newG: Int
            var newB: Int
            val pixels = IntArray(width * height)
            bitmap.getPixels(pixels, 0, width, 0, 0, width, height)
            var pos: Int
            var i = 1
            val length = height - 1
            while (i < length) {
                var k = 1
                val len = width - 1
                while (k < len) {
                    pos = i * width + k
                    pixColor = pixels[pos]
                    pixR = Color.red(pixColor)
                    pixG = Color.green(pixColor)
                    pixB = Color.blue(pixColor)
                    newR = MAX_VALUE - pixR
                    newG = MAX_VALUE - pixG
                    newB = MAX_VALUE - pixB
                    newR = Math.min(MAX_VALUE, Math.max(0, newR))
                    newG = Math.min(MAX_VALUE, Math.max(0, newG))
                    newB = Math.min(MAX_VALUE, Math.max(0, newB))
                    pixels[pos] = Color.argb(MAX_VALUE, newR, newG, newB)
                    k++
                }
                i++
            }
            val newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
            newBitmap.setPixels(pixels, 0, width, 0, 0, width, height)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "film")
        }
        return null
    }

    /**
     * 柔化效果处理
     * <pre>
     * delta 取值范围只要大于等于 1 就可以, 但是避免太大, 导致变得很暗, 限制 1-24
    </pre> *
     * @param bitmap 待操作源图片
     * @param delta  图片的亮暗程度值, 越小图片会越亮 1..24
     * @return 柔化效果处理后的图片
     */
    fun soften(
        bitmap: Bitmap,
        delta: Int
    ): Bitmap? {
        if (delta > 24 || delta <= 0) return null
        try {
            // 高斯矩阵
            val gauss = intArrayOf(1, 2, 1, 2, 4, 2, 1, 2, 1)
            val width = bitmap.width
            val height = bitmap.height
            var pixColor: Int
            var pixR: Int
            var pixG: Int
            var pixB: Int
            var newR = 0
            var newG = 0
            var newB = 0
            var idx: Int
            val pixels = IntArray(width * height)
            bitmap.getPixels(pixels, 0, width, 0, 0, width, height)
            var i = 1
            val length = height - 1
            while (i < length) {
                var k = 1
                val len = width - 1
                while (k < len) {
                    idx = 0
                    for (m in -1..1) {
                        for (n in -1..1) {
                            pixColor = pixels[(i + m) * width + k + n]
                            pixR = Color.red(pixColor)
                            pixG = Color.green(pixColor)
                            pixB = Color.blue(pixColor)
                            newR += pixR * gauss[idx]
                            newG += pixG * gauss[idx]
                            newB += pixB * gauss[idx]
                            idx++
                        }
                    }
                    newR /= delta
                    newG /= delta
                    newB /= delta
                    newR = Math.min(255, Math.max(0, newR))
                    newG = Math.min(255, Math.max(0, newG))
                    newB = Math.min(255, Math.max(0, newB))
                    pixels[i * width + k] = Color.argb(255, newR, newG, newB)
                    newR = 0
                    newG = 0
                    newB = 0
                    k++
                }
                i++
            }
            val newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
            newBitmap.setPixels(pixels, 0, width, 0, 0, width, height)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "soften")
        }
        return null
    }

    /**
     * 锐化效果处理
     * @param bitmap 待操作源图片
     * @return 锐化效果处理后的图片
     */
    fun sharpen(bitmap: Bitmap): Bitmap? {
        try {
            // 拉普拉斯矩阵
            val laplacian = intArrayOf(-1, -1, -1, -1, 9, -1, -1, -1, -1)
            val width = bitmap.width
            val height = bitmap.height
            var pixColor: Int
            var pixR: Int
            var pixG: Int
            var pixB: Int
            var newR = 0
            var newG = 0
            var newB = 0
            var idx: Int
            val alpha = 0.3f
            val pixels = IntArray(width * height)
            bitmap.getPixels(pixels, 0, width, 0, 0, width, height)
            var i = 1
            val length = height - 1
            while (i < length) {
                var k = 1
                val len = width - 1
                while (k < len) {
                    idx = 0
                    for (m in -1..1) {
                        for (n in -1..1) {
                            pixColor = pixels[(i + n) * width + k + m]
                            pixR = Color.red(pixColor)
                            pixG = Color.green(pixColor)
                            pixB = Color.blue(pixColor)
                            newR += (pixR * laplacian[idx] * alpha).toInt()
                            newG += (pixG * laplacian[idx] * alpha).toInt()
                            newB += (pixB * laplacian[idx] * alpha).toInt()
                            idx++
                        }
                    }
                    newR = Math.min(255, Math.max(0, newR))
                    newG = Math.min(255, Math.max(0, newG))
                    newB = Math.min(255, Math.max(0, newB))
                    pixels[i * width + k] = Color.argb(255, newR, newG, newB)
                    newR = 0
                    newG = 0
                    newB = 0
                    k++
                }
                i++
            }
            val newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
            newBitmap.setPixels(pixels, 0, width, 0, 0, width, height)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "sharpen")
        }
        return null
    }

    /**
     * 浮雕效果处理
     * @param bitmap 待操作源图片
     * @return 浮雕效果处理后的图片
     */
    fun emboss(bitmap: Bitmap): Bitmap? {
        try {
            val width = bitmap.width
            val height = bitmap.height
            var pixColor: Int
            var pixR: Int
            var pixG: Int
            var pixB: Int
            var newR: Int
            var newG: Int
            var newB: Int
            val pixels = IntArray(width * height)
            bitmap.getPixels(pixels, 0, width, 0, 0, width, height)
            var pos: Int
            var i = 1
            val length = height - 1
            while (i < length) {
                var k = 1
                val len = width - 1
                while (k < len) {
                    pos = i * width + k
                    pixColor = pixels[pos]
                    pixR = Color.red(pixColor)
                    pixG = Color.green(pixColor)
                    pixB = Color.blue(pixColor)
                    pixColor = pixels[pos + 1]
                    newR = Color.red(pixColor) - pixR + 127
                    newG = Color.green(pixColor) - pixG + 127
                    newB = Color.blue(pixColor) - pixB + 127
                    newR = Math.min(255, Math.max(0, newR))
                    newG = Math.min(255, Math.max(0, newG))
                    newB = Math.min(255, Math.max(0, newB))
                    pixels[pos] = Color.argb(255, newR, newG, newB)
                    k++
                }
                i++
            }
            val newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
            newBitmap.setPixels(pixels, 0, width, 0, 0, width, height)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "emboss")
        }
        return null
    }


    //    /**
    //     * 转为灰度图片
    //     * @param bitmap 待操作源图片
    //     * @return 灰度图
    //     */
    //    public static Bitmap gray(final Bitmap bitmap) {
    //        ;
    //        try {
    //            int width = bitmap.getWidth(); // 获取位图的宽
    //            int height = bitmap.getHeight(); // 获取位图的高
    //            int[] pixels = new int[width * height]; // 通过位图的大小创建像素点数组
    //            bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
    //
    //            int alpha = 0xFF << 24; // 默认将 bitmap 当成 24 色图片
    //            for (int i = 0; i < height; i++) {
    //                for (int j = 0; j < width; j++) {
    //                    int grey = pixels[width * i + j];
    //
    //                    int red = ((grey & 0x00FF0000) >> 16);
    //                    int green = ((grey & 0x0000FF00) >> 8);
    //                    int blue = (grey & 0x000000FF);
    //
    //                    grey = (int) ((float) red * 0.3 + (float) green * 0.59 + (float) blue * 0.11);
    //                    grey = alpha | (grey << 16) | (grey << 8) | grey;
    //                    pixels[width * i + j] = grey;
    //                }
    //            }
    //            Bitmap newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
    //            newBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
    //            return newBitmap;
    //        } catch (Exception e) {
    //            Timber.d( e, "gray");
    //        }
    //        return null;
    //    }
    /**
     * 转为灰度图片
     * @param bitmap 待操作源图片
     * @return 灰度图
     */
    fun gray(bitmap: Bitmap): Bitmap? {
        
        try {
            val newBitmap = Bitmap.createBitmap(
                bitmap.width, bitmap.height, bitmap.config
            )
            val canvas = Canvas(newBitmap)
            val paint = Paint()
            val colorMatrix = ColorMatrix()
            colorMatrix.setSaturation(0f)
            val colorMatrixColorFilter = ColorMatrixColorFilter(colorMatrix)
            paint.colorFilter = colorMatrixColorFilter
            canvas.drawBitmap(bitmap, 0f, 0f, paint)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "gray")
        }
        return null
    }

    // =======
    // = 色彩 =
    // =======
    /**
     * 饱和度处理
     * @param bitmap          待操作源图片
     * @param saturationValue 新的饱和度值
     * @return 改变了饱和度值之后的图片
     */
    fun saturation(
        bitmap: Bitmap,
        saturationValue: Int
    ): Bitmap? {
        try {
            // 计算出符合要求的饱和度值
            val newSaturationValue = saturationValue * 1.0f / 127
            // 创建一个颜色矩阵
            val saturationColorMatrix = ColorMatrix()
            // 设置饱和度值
            saturationColorMatrix.setSaturation(newSaturationValue)
            // 创建一个画笔并设置其颜色过滤器
            val paint = Paint()
            paint.colorFilter = ColorMatrixColorFilter(saturationColorMatrix)
            // 创建一个新的图片并创建画布
            val newBitmap = Bitmap.createBitmap(
                bitmap.width, bitmap.height,
                Bitmap.Config.ARGB_8888
            )
            val canvas = Canvas(newBitmap)
            // 将源图片使用给定的画笔画到画布上
            canvas.drawBitmap(bitmap, 0f, 0f, paint)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "saturation")
        }
        return null
    }

    /**
     * 亮度处理
     * @param bitmap   待操作源图片
     * @param lumValue 新的亮度值
     * @return 改变了亮度值之后的图片
     */
    fun lum(
        bitmap: Bitmap,
        lumValue: Int
    ): Bitmap? {
        try {
            // 计算出符合要求的亮度值
            val newLumValue = lumValue * 1.0f / 127
            // 创建一个颜色矩阵
            val lumColorMatrix = ColorMatrix()
            // 设置亮度值
            lumColorMatrix.setScale(newLumValue, newLumValue, newLumValue, 1f)
            // 创建一个画笔并设置其颜色过滤器
            val paint = Paint()
            paint.colorFilter = ColorMatrixColorFilter(lumColorMatrix)
            // 创建一个新的图片并创建画布
            val newBitmap = Bitmap.createBitmap(
                bitmap.width, bitmap.height,
                Bitmap.Config.ARGB_8888
            )
            val canvas = Canvas(newBitmap)
            // 将源图片使用给定的画笔画到画布上
            canvas.drawBitmap(bitmap, 0f, 0f, paint)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "lum")
        }
        return null
    }

    /**
     * 色相处理
     * @param bitmap   待操作源图片
     * @param hueValue 新的色相值
     * @return 改变了色相值之后的图片
     */
    fun hue(
        bitmap: Bitmap,
        hueValue: Int
    ): Bitmap? {
        try {
            // 计算出符合要求的色相值
            val newHueValue = (hueValue - 127) * 1.0f / 127 * 180
            // 创建一个颜色矩阵
            val hueColorMatrix = ColorMatrix()
            // 控制让红色区在色轮上旋转的角度
            hueColorMatrix.setRotate(0, newHueValue)
            // 控制让绿红色区在色轮上旋转的角度
            hueColorMatrix.setRotate(1, newHueValue)
            // 控制让蓝色区在色轮上旋转的角度
            hueColorMatrix.setRotate(2, newHueValue)
            // 创建一个画笔并设置其颜色过滤器
            val paint = Paint()
            paint.colorFilter = ColorMatrixColorFilter(hueColorMatrix)
            // 创建一个新的图片并创建画布
            val newBitmap = Bitmap.createBitmap(
                bitmap.width, bitmap.height,
                Bitmap.Config.ARGB_8888
            )
            val canvas = Canvas(newBitmap)
            // 将源图片使用给定的画笔画到画布上
            canvas.drawBitmap(bitmap, 0f, 0f, paint)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "hue")
        }
        return null
    }

    /**
     * 亮度、色相、饱和度处理
     * @param bitmap          待操作源图片
     * @param lumValue        亮度值
     * @param hueValue        色相值
     * @param saturationValue 饱和度值
     * @return 亮度、色相、饱和度处理后的图片
     */
    fun lumHueSaturation(
        bitmap: Bitmap,
        lumValue: Int,
        hueValue: Int,
        saturationValue: Int
    ): Bitmap? {
        try {
            // 计算出符合要求的饱和度值
            val newSaturationValue = saturationValue * 1.0f / 127
            // 计算出符合要求的亮度值
            val newLumValue = lumValue * 1.0f / 127
            // 计算出符合要求的色相值
            val newHueValue = (hueValue - 127) * 1.0f / 127 * 180
            // 创建一个颜色矩阵并设置其饱和度
            val colorMatrix = ColorMatrix()
            // 设置饱和度值
            colorMatrix.setSaturation(newSaturationValue)
            // 设置亮度值
            colorMatrix.setScale(newLumValue, newLumValue, newLumValue, 1f)
            // 控制让红色区在色轮上旋转的角度
            colorMatrix.setRotate(0, newHueValue)
            // 控制让绿红色区在色轮上旋转的角度
            colorMatrix.setRotate(1, newHueValue)
            // 控制让蓝色区在色轮上旋转的角度
            colorMatrix.setRotate(2, newHueValue)
            // 创建一个画笔并设置其颜色过滤器
            val paint = Paint()
            paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
            // 创建一个新的图片并创建画布
            val newBitmap = Bitmap.createBitmap(
                bitmap.width, bitmap.height,
                Bitmap.Config.ARGB_8888
            )
            val canvas = Canvas(newBitmap)
            // 将源图片使用给定的画笔画到画布上
            canvas.drawBitmap(bitmap, 0f, 0f, paint)
            return newBitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "lumHueSaturation")
        }
        return null
    }

    // =

    // =
    /**
     * 将 YUV 格式的图片的源数据从横屏模式转为竖屏模式
     * <pre>
     * 注: 将源图片的宽高互换就是新图片的宽高
    </pre> *
     * @param sourceData YUV 格式的图片的源数据
     * @param width      宽
     * @param height     高
     * @return byte[]
     */
    fun yuvLandscapeToPortrait(
        sourceData: ByteArray,
        width: Int,
        height: Int
    ): ByteArray? {
        if (sourceData.isEmpty()) return null
        val rotatedData = ByteArray(sourceData.size)
        for (y in 0 until height) {
            for (x in 0 until width) {
                rotatedData[x * height + height - y - 1] = sourceData[x + y * width]
            }
        }
        return rotatedData
    }

    /**
     * 通过 View Cache 绘制为 Bitmap
     * @param view [View]
     * @return [Bitmap]
     */
    fun getBitmapFromViewCache(view: View): Bitmap? {
        try {
            // 清除视图焦点
            view.clearFocus()
            // 将视图设为不可点击
            view.isPressed = false

            // 获取视图是否可以保存画图缓存
            val willNotCache = view.willNotCacheDrawing()
            view.setWillNotCacheDrawing(false)

            // 获取绘制缓存位图的背景颜色
            val color = view.drawingCacheBackgroundColor
            // 设置绘图背景颜色
            view.drawingCacheBackgroundColor = 0
            if (color != 0) { // 获取的背景不是黑色的则释放以前的绘图缓存
                view.destroyDrawingCache() // 释放绘图资源所使用的缓存
            }

            // 重新创建绘图缓存, 此时的背景色是黑色
            view.buildDrawingCache()
            // 获取绘图缓存, 注意这里得到的只是一个图像的引用
            val cacheBitmap = view.drawingCache ?: return null
            val bitmap = Bitmap.createBitmap(cacheBitmap)
            // 释放位图内存
            view.destroyDrawingCache()
            // 回滚以前的缓存设置、缓存颜色设置
            view.setWillNotCacheDrawing(willNotCache)
            view.drawingCacheBackgroundColor = color
            return bitmap
        } catch (e: java.lang.Exception) {
            Timber.d( e, "getBitmapFromViewCache")
        }
        return null
    }

    /**
     * 动态设置图片的主题色（保留明暗关系）
     */
    fun handleImageEffect(bitmap: Bitmap, targetColor: Int): Bitmap? {
        //由于不能直接在原图上修改，所以创建一个图片，设定宽度高度与原图相同。为32位ARGB图片
        val currentBitmap = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
        //创建一个和原图相同大小的画布
        val canvas = Canvas(currentBitmap)
        //创建笔刷并设置抗锯齿
        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        val hue0 = getHub(Color.parseColor("#61BCF8"), targetColor)
        val cm = ColorMatrix()
        val tmp = ColorMatrix()
        cm.setRGB2YUV()
        tmp.setRotate(0, hue0)
        cm.postConcat(tmp)
        tmp.setYUV2RGB()
        cm.postConcat(tmp)
        paint.colorFilter = ColorMatrixColorFilter(cm)
        canvas.drawBitmap(bitmap, 0f, 0f, paint)
        return currentBitmap
    }


    fun getHub(fromColor: Int, targetColor: Int): Float {
        val yuv1 = convertRGB2YUV(fromColor)
        val yuv2 = convertRGB2YUV(targetColor)
        //计算两个颜色的uv分量组成的向量之间的夹角
        return getDegreeBetweenVectors(intArrayOf(yuv1[1], yuv1[2]), intArrayOf(yuv2[1], yuv2[2]))
    }

    /**
     * 计算向量1，顺时针旋转多少度可以得到向量2
     * 返回的度数为0到360度
     * @param vs1
     * @param vs2
     * @return
     */
    private fun getDegreeBetweenVectors(vs1: IntArray, vs2: IntArray): Float {
        var cosDegree = 0.0
        //向量的内积
        val nj = vs1[0] * vs2[0] + vs1[1] * vs2[1]
        //叉积
        val cj = vs1[0] * vs2[1] - vs1[1] * vs2[0]
        val bl =
            Math.sqrt((vs1[0] * vs1[0] + vs1[1] * vs1[1]).toDouble()) * Math.sqrt((vs2[0] * vs2[0] + vs2[1] * vs2[1]).toDouble())
        cosDegree = nj / bl
        var degree = Math.acos(cosDegree) / Math.PI * 180
        //叉积大于0，表示向量2 在向量1的左边
        degree = (if (cj > 0) 360 - degree else degree).toFloat().toDouble()
        return degree.toFloat()
    }


    private fun convertRGB2YUV(color: Int): IntArray {
        val cm = ColorMatrix()
        cm.setRGB2YUV()
        val yuvArray = cm.array
        val r = Color.red(color)
        val g = Color.green(color)
        val b = Color.blue(color)
        val result = IntArray(3)
        result[0] = floatToByte(yuvArray[0] * r + yuvArray[1] * g + yuvArray[2] * b)
        result[1] = floatToByte(yuvArray[5] * r + yuvArray[6] * g + yuvArray[7] * b)
        result[2] = floatToByte(yuvArray[10] * r + yuvArray[11] * g + yuvArray[12] * b)
        return result
    }

    private fun floatToByte(x: Float): Int {
        return x.roundToInt()
    }

    /**
     * 逐行扫描 清除边界空白
     *
     * @param blank1 边距留多少个像素
     * @param color 背景色限定
     * @return 清除边界后的Bitmap
     */
    fun clearBlank(mBitmap: Bitmap, blank1: Int, color: Int): Bitmap? {
            var blank = blank1
            val height = mBitmap.height
            val width = mBitmap.width
            var top = 0
            var left = 0
            var right = 0
            var bottom = 0
            var widthPixels = IntArray(width)
            var isStop: Boolean
            for (y in 0 until height) {
                mBitmap.getPixels(widthPixels, 0, width, 0, y, width, 1)
                isStop = false
                for (pix in widthPixels) {
                    if (pix != color) {
                        top = y
                        isStop = true
                        break
                    }
                }
                if (isStop) {
                    break
                }
            }
            for (y in height - 1 downTo 0) {
                mBitmap.getPixels(widthPixels, 0, width, 0, y, width, 1)
                isStop = false
                for (pix in widthPixels) {
                    if (pix != color) {
                        bottom = y
                        isStop = true
                        break
                    }
                }
                if (isStop) {
                    break
                }
            }
            widthPixels = IntArray(height)
            for (x in 0 until width) {
                mBitmap.getPixels(widthPixels, 0, 1, x, 0, 1, height)
                isStop = false
                for (pix in widthPixels) {
                    if (pix != color) {
                        left = x
                        isStop = true
                        break
                    }
                }
                if (isStop) {
                    break
                }
            }
            for (x in width - 1 downTo 1) {
                mBitmap.getPixels(widthPixels, 0, 1, x, 0, 1, height)
                isStop = false
                for (pix in widthPixels) {
                    if (pix != color) {
                        right = x
                        isStop = true
                        break
                    }
                }
                if (isStop) {
                    break
                }
            }
            if (blank < 0) {
                blank = 0
            }
            left = if (left - blank > 0) left - blank else 0
            top = if (top - blank > 0) top - blank else 0
            right = if (right + blank > width - 1) width - 1 else right + blank
            bottom = if (bottom + blank > height - 1) height - 1 else bottom + blank
        return  Bitmap.createBitmap(mBitmap, left, top, right - left, bottom - top)

    }

    /**
     * 清除bitmap左右边界空白
     *
     * @param mBitmap 源图
     * @param blank1   边距留多少个像素
     * @param color   背景色限定
     * @return 清除后的bitmap
     */
    fun clearLRBlank(mBitmap: Bitmap, blank1: Int, color: Int): Bitmap? {
            var blank = blank1
            val height = mBitmap.height
            val width = mBitmap.width
            var left = 0
            var right = 0
            val pixs = IntArray(height)
            var isStop: Boolean
            for (x in 0 until width) {
                mBitmap.getPixels(pixs, 0, 1, x, 0, 1, height)
                isStop = false
                for (pix in pixs) {
                    if (pix != color) {
                        left = x
                        isStop = true
                        break
                    }
                }
                if (isStop) {
                    break
                }
            }
            for (x in width - 1 downTo 1) {
                mBitmap.getPixels(pixs, 0, 1, x, 0, 1, height)
                isStop = false
                for (pix in pixs) {
                    if (pix != color) {
                        right = x
                        isStop = true
                        break
                    }
                }
                if (isStop) {
                    break
                }
            }
            if (blank < 0) {
                blank = 0
            }
            left = if (left - blank > 0) left - blank else 0
            right = if (right + blank > width - 1) width - 1 else right + blank
        return  Bitmap.createBitmap(mBitmap, left, 0, right - left, height)

    }

    /**
     * 给Bitmap添加背景色
     *
     * @param srcBitmap 源图
     * @param color     背景颜色
     * @return 修改背景后的bitmap
     */
    fun drawBgToBitmap(srcBitmap: Bitmap, color: Int): Bitmap {
        val paint = Paint()
        paint.color = color
        val bitmap = Bitmap.createBitmap(srcBitmap.width, srcBitmap.height, srcBitmap.config)
        val canvas = Canvas(bitmap)
        canvas.drawRect(0f, 0f, srcBitmap.width.toFloat(), srcBitmap.height.toFloat(), paint)
        canvas.drawBitmap(srcBitmap, 0f, 0f, paint)
        return bitmap
    }

}



/**
 *bitmap转base64
 **/
fun String.base64ToBitmap(): Bitmap {
    val bytes: ByteArray = Base64.decode(this, Base64.DEFAULT)
    return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
}

/**
 * bitmap转为base64
 *
 * @return
 */
fun Bitmap.toBase64(): String {
    var result: String? = null
    var baos: ByteArrayOutputStream? = null
    try {
        baos = ByteArrayOutputStream()
        this.compress(Bitmap.CompressFormat.JPEG, 100, baos)
        baos.flush()
        baos.close()
        val bitmapBytes: ByteArray = baos.toByteArray()
        result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT)
    } catch (e: IOException) {
        e.printStackTrace()
    } finally {
        try {
            if (baos != null) {
                baos.flush()
                baos.close()
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    return result?:""
}

/**
 * Bitmap to bytes.
 *
 * @param format  The format of bitmap.
 * @param quality The quality.
 * @return bytes
 */
fun Bitmap.bitmap2Bytes(
    format: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG,
    quality: Int = 100
): ByteArray {
    val bass = ByteArrayOutputStream()
    compress(format, quality, bass)
    return bass.toByteArray()
}

/**
 * Bytes to bitmap.
 *
 * @return bitmap
 */
fun ByteArray.bytes2Bitmap( ): Bitmap {
    return BitmapFactory.decodeByteArray(this, 0, this.size)
}

/**
 * Save the bitmap.
 *
 * @param src     The source of bitmap.
 * @param file    The file.
 * @param format  The format of the image.
 * @param quality Hint to the compressor, 0-100. 0 meaning compress for
 * small size, 100 meaning compress for max quality. Some
 * formats, like PNG which is lossless, will ignore the
 * quality setting
 * @param recycle True to recycle the source of bitmap, false otherwise.
 * @return `true`: success<br></br>`false`: fail
 */
fun Bitmap.save(
    file: File,
    format: CompressFormat= CompressFormat.JPEG,
    quality: Int=1,
    recycle: Boolean=true
): Boolean {
    if (DrawableExt.isEmptyBitmap(this)) {
        Timber.e("ImageUtils", "bitmap is empty.")
        return false
    }
    if (isRecycled) {
        Timber.e("ImageUtils", "bitmap is recycled.")
        return false
    }
    if (!FileManager.createFileByDeleteOldFile(file)) {
        Timber.e("ImageUtils", "create or delete file <$file> failed.")
        return false
    }
    var os: OutputStream? = null
    var ret = false
    try {
        os = BufferedOutputStream(FileOutputStream(file))
        ret = compress(format, quality, os)
        if (recycle && !isRecycled) recycle()
    } catch (e: IOException) {
        e.printStackTrace()
    } finally {
        try {
            os?.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    return ret
}

fun Bitmap.rotateBitmap(
    rotationDegrees: Int, flipX: Boolean, flipY: Boolean
): Bitmap {
    val matrix = Matrix()

    // Rotate the image back to straight.
    matrix.postRotate(rotationDegrees.toFloat())

    // Mirror the image along the X or Y axis.
    matrix.postScale(if (flipX) -1.0f else 1.0f, if (flipY) -1.0f else 1.0f)
    val rotatedBitmap = Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)

    // Recycle the old bitmap if it has changed.
    if (rotatedBitmap != this) {
        this.recycle()
    }
    return rotatedBitmap
}

/*
internal suspend fun Bitmap.calculateLuminance(): Double? {
    val color = Color.MAGENTA
    return withContext(Dispatchers.Default) {
        val alpha = replaceAlphaWithColor(color)
        return@withContext alpha.getLuminance(color)
    }
}

private fun Bitmap.replaceAlphaWithColor(@ColorInt color: Int): Bitmap {
    val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    result.eraseColor(color)
    Canvas(result).apply {
        drawBitmap(this@replaceAlphaWithColor, Matrix(), BITMAP_PAINT)
    }
    return result
}

private fun Bitmap.getLuminance(@ColorInt alphaSubstitute: Int): Double? {
    val imagePalette = Palette.from(this)
        .clearFilters()
        .addFilter { rgb, _ -> (rgb != alphaSubstitute) }
        .generate()
    val dominantSwatch = imagePalette.dominantSwatch
    return dominantSwatch?.rgb?.let(ColorUtils::calculateLuminance)
}*/

@Suppress("DEPRECATION")
internal val Bitmap.Config?.bytesPerPixel: Int
    get() = when {
        this == Bitmap.Config.ALPHA_8 -> 1
        this == Bitmap.Config.RGB_565 -> 2
        this == Bitmap.Config.ARGB_4444 -> 2
        Build.VERSION.SDK_INT >= 26 && this == Bitmap.Config.RGBA_F16 -> 8
        else -> 4
    }

/**
 * Returns the in memory size of this [Bitmap] in bytes.
 * This value will not change over the lifetime of a bitmap.
 */
val Bitmap.allocationByteCountCompat: Int
    get() {
        check(!isRecycled) {
            "Cannot obtain size for recycled bitmap: $this [$width x $height] + $config"
        }

        return try {
            allocationByteCount
        } catch (_: Exception) {
            width * height * config.bytesPerPixel
        }
    }

val Bitmap.Config.isHardware: Boolean
    get() = Build.VERSION.SDK_INT >= 26 && this == Bitmap.Config.HARDWARE

/**
 * 水平翻转图片 ( 左右颠倒 )
 * @param bitmap 待操作源图片
 * @return 翻转后的图片
 */
fun Bitmap.reverseByHorizontal(): Bitmap {
    return reverse(true)
}

/**
 * 垂直翻转图片 ( 上下颠倒 )
 * @param bitmap 待操作源图片
 * @return 翻转后的图片
 */
fun Bitmap.reverseByVertical(): Bitmap {
    return reverse(false)
}

/**
 * 翻转图片
 * @param bitmap     待操作源图片
 * @param horizontal 是否水平翻转
 * @return 翻转后的图片
 */
fun Bitmap.reverse(
    horizontal: Boolean
): Bitmap {
    val matrix = Matrix()
    if (horizontal) {
        matrix.preScale(-1f, 1f)
    } else {
        matrix.preScale(1f, -1f)
    }
    return Bitmap.createBitmap(this, 0, 0, width, height, matrix, false)
}

/**
 * 重新编码 Bitmap
 * @param bitmap  需要重新编码的 bitmap
 * @param format  编码后的格式 如 Bitmap.CompressFormat.PNG
 * @param quality 质量 (0..100)
 * @param options [BitmapFactory.Options]
 * @return 重新编码后的图片
 */
fun Bitmap.recode(
    format: Bitmap.CompressFormat,
    quality: Int,
    options: BitmapFactory.Options?
): Bitmap? {
    try {
        val baos = ByteArrayOutputStream()
        compress(format, quality, baos)
        val data = baos.toByteArray()
        return BitmapFactory.decodeByteArray(data, 0, data.size, options)
    } catch (e: java.lang.Exception) {
        Timber.d( e, "recode")
    }
    return null
}

/**
 * 缩放图片 ( 指定所需宽高 )
 * @param bitmap    待操作源图片
 * @param newWidth  新宽度
 * @param newHeight 新高度
 * @return 缩放后的图片
 */
fun Bitmap.zoom(
    newWidth: Int,
    newHeight: Int = newWidth
): Bitmap {
    return  Bitmap.createScaledBitmap(this, newWidth, newHeight, true)
}

/**
 * 倾斜图片
 * <pre>
 * 倾斜因子 以小数点倾斜 如: 0.1 防止数值过大 Canvas: trying to draw too large
</pre> *
 * @param kx     X 轴倾斜因子
 * @param ky     Y 轴倾斜因子
 * @param px     X 轴轴心点
 * @param py     Y 轴轴心点
 * @return 倾斜后的图片
 */
fun Bitmap.skew(
    kx: Float,
    ky: Float,
    px: Float =0f,
    py: Float =0f
): Bitmap {
    val matrix = Matrix()
    matrix.setSkew(kx, ky, px, py)
    return Bitmap.createBitmap(
        this, 0, 0,
        width, height,
        matrix, true
    )
}

/**
 * 裁剪图片
 * @param bitmap 待操作源图片
 * @param x      X 轴开始坐标
 * @param y      Y 轴开始坐标
 * @param width  裁剪宽度
 * @param height 裁剪高度
 * @return 裁剪后的图片
 */
fun Bitmap.clip(
    x: Int = 0,
    y: Int = 0,
    width: Int,
    height: Int
): Bitmap {
    return  Bitmap.createBitmap(this, x, y, width, height)
}

/**
 * 合并图片
 * @param bgd      后景 Bitmap
 * @param fg       前景 Bitmap
 * @param mode     合并模式 [PorterDuff.Mode]
 * @param bgdPoint 后景绘制 left、top 坐标
 * @param fgPoint  前景绘制 left、top 坐标
 * @return 合并后的图片
 */
fun Bitmap.combine(
    fg: Bitmap,
    mode: PorterDuff.Mode? = null,
    bgdPoint: Point? = null,
    fgPoint: Point? = null
): Bitmap {
    val width = width.coerceAtLeast(fg.width)
    val height = height.coerceAtLeast(fg.height)
    val paint = Paint()
    if (mode != null) {
        paint.xfermode = PorterDuffXfermode(mode)
    }
    val newBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(newBitmap)
    canvas.drawBitmap(
        this, (bgdPoint?.x ?: 0).toFloat(),
        (bgdPoint?.y ?: 0).toFloat(), null)
    canvas.drawBitmap(fg, (fgPoint?.x ?: 0).toFloat(),
        (fgPoint?.y ?: 0).toFloat(), paint)
    return newBitmap
}

/**
 * 合并图片 ( 居中 )
 * @param bgd  后景 Bitmap
 * @param fg   前景 Bitmap
 * @param mode 合并模式 [PorterDuff.Mode]
 * @return 合并后的图片
 */
fun Bitmap.combineToCenter(
    fg: Bitmap,
    mode: PorterDuff.Mode? = null
): Bitmap {
    // 绘制坐标点
    val bgdPoint = Point()
    val fgPoint = Point()

    // 宽高信息
    val bgdWidth = width
    val bgdHeight = height
    val fgWidth = fg.width
    val fgHeight = fg.height
    if (bgdWidth > fgWidth) {
        fgPoint.x = (bgdWidth - fgWidth) / 2
    } else {
        bgdPoint.x = (fgWidth - bgdWidth) / 2
    }
    if (bgdHeight > fgHeight) {
        fgPoint.y = (bgdHeight - fgHeight) / 2
    } else {
        bgdPoint.y = (fgHeight - bgdHeight) / 2
    }
    return combine(fg, mode, bgdPoint, fgPoint)
}

/**
 * 合并图片 ( 转为相同大小 )
 * @param bgd  后景 Bitmap
 * @param fg1   前景 Bitmap
 * @param mode 合并模式 [PorterDuff.Mode]
 * @return 合并后的图片
 */
fun Bitmap.combineToSameSize(
    fg1: Bitmap,
    mode: PorterDuff.Mode? = null
): Bitmap {
    var bgd = this
    var fg = fg1
    val width = bgd.width.coerceAtMost(fg.width)
    val height = bgd.height.coerceAtMost(fg.height)
    if (fg.width != width && fg.height != height) {
        fg = fg.zoom(width, height)
    }
    if (bgd.width != width && bgd.height != height) {
        bgd = bgd.zoom(width, height)
    }
    val paint = Paint()
    if (mode != null) {
        paint.xfermode = PorterDuffXfermode(mode)
    }
    val newBitmap = Bitmap.createBitmap(
        width, height, Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(newBitmap)
    canvas.drawBitmap(bgd, 0f, 0f, null)
    canvas.drawBitmap(fg, 0f, 0f, paint)
    return newBitmap
}


/**
 * 图片倒影处理
 * @param bitmap            待操作源图片
 * @param reflectionSpacing 源图片与倒影之间的间距
 * @param reflectionHeight  倒影高度
 * @return 倒影处理后的图片
 */
fun Bitmap.reflection(
    reflectionSpacing: Int = 1,
    reflectionHeight: Int = height
): Bitmap {

    // 创建画布, 画布分为上中下三部分, 上: 是源图片, 中: 是源图片与倒影的间距, 下: 是倒影

    // 创建倒影图片
    val reflectionImage: Bitmap = reverseByVertical() // 垂直翻转图片 ( 上下颠倒 )
    // 创建一张宽度与源图片相同, 但高度等于 源图片的高度 + 间距 + 倒影的高度的图片
    val bitmapWithReflection = Bitmap.createBitmap(
        width, height + reflectionSpacing + reflectionHeight,
        Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmapWithReflection)

    // 将源图片画到画布的上半部分, 将倒影画到画布的下半部分, 倒影与画布顶部的间距是源图片的高度加上源图片与倒影之间的间距
    canvas.drawBitmap(this, 0f, 0f, null)
    canvas.drawBitmap(reflectionImage, 0f, (height + reflectionSpacing).toFloat(), null)

    reflectionImage.recycle()

    // 边距负数处理
    val spacing = reflectionSpacing.coerceAtLeast(0)

    // 将倒影改成半透明, 创建画笔, 并设置画笔的渐变从半透明的白色到全透明的白色, 然后再倒影上面画半透明效果
    val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    paint.shader = LinearGradient(0f, height.toFloat(), 0f,
        (bitmapWithReflection.height + spacing).toFloat(), 0x70ffffff, 0x00ffffff, Shader.TileMode.CLAMP)
    paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
    canvas.drawRect(0f, (height + spacing).toFloat(), width.toFloat(),
        (bitmapWithReflection.height + spacing).toFloat(), paint)
    return bitmapWithReflection
}

fun Bitmap.toBitmapShader(tileX: Shader.TileMode=Shader.TileMode.REPEAT, tileY: Shader.TileMode=Shader.TileMode.REPEAT) = BitmapShader(this,tileX, tileY)